﻿using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.Xrm.Sdk;
using Microsoft.Xrm.Sdk.Messages;

namespace CrmPluginLogger.Logger.CRM
{
    public class CrmPluginLogger
    {
        #region CRM Attributes

        private const string NameOfEntity = "andz_pluginlog";
        private const string EntityId = "andz_pluginlogid";
        private const string Name = "andz_name";
        private const string LogTime = "andz_logtime";
        private const string ExceptionMessage = "andz_exceptionmessage";
        private const string PluginContextInfo = "andz_plugincontextinfo";
        private const string StackTrace = "andz_stacktrace";
        private const string ExceptionType = "andz_exceptiontype";
        #endregion

        /// <summary>
        /// Logs the plugin exception.
        /// </summary>
        /// <param name="ex">The ex.</param>
        /// <param name="context">The context.</param>
        /// <param name="organizationService">The organization service.</param>
        /// <returns></returns>
        public static void LogPluginException(Exception ex, IPluginExecutionContext context, IOrganizationService organizationService)
        {
            var pluginLog = new Entity(NameOfEntity);

            #region Conditional Handling (if required)
            //if (ex is InvalidPluginExecutionException)
            //{

            //}
            //else if (ex is SoapException)
            //{

            //}
            //else
            //{

            //}
            #endregion

            pluginLog.Attributes[LogTime] = DateTime.UtcNow;
            pluginLog.Attributes[ExceptionMessage] = ex.Message;
            pluginLog.Attributes[StackTrace] = ex.StackTrace;
            pluginLog.Attributes[PluginContextInfo] = GatherPluginContextInfo(context);
            pluginLog.Attributes[ExceptionType] = ex.GetType().ToString();
            pluginLog.Attributes[Name] = "[" + context.PrimaryEntityName + "]" + ex.Message + " | " + DateTime.UtcNow;

            // Create an ExecuteMultipleRequest object.
            var multipleRequest = new ExecuteMultipleRequest
            {
                // Assign settings that define execution behavior: continue on error, return responses. 
                Settings = new ExecuteMultipleSettings
                {
                    ContinueOnError = true,
                    ReturnResponses = false
                },
                // Create an empty organization request collection.
                Requests = new OrganizationRequestCollection()
            };

            var createRequest = new CreateRequest { Target = pluginLog };
            multipleRequest.Requests.Add(createRequest);

            // Execute all the requests in the request collection using a single web method call.
            organizationService.Execute(multipleRequest);
        }

        /// <summary>
        /// Gathers the plugin context information.
        /// </summary>
        /// <param name="context">The context.</param>
        /// <returns></returns>
        private static string GatherPluginContextInfo(IPluginExecutionContext context)
        {
            var contextInfoDetails = new List<String>();
            var target = GetTarget(context);

            contextInfoDetails.Add("MessageName: " + context.MessageName);
            contextInfoDetails.Add("IsolationMode: " + context.IsolationMode);
            contextInfoDetails.Add("PrimaryEntityId: " + context.PrimaryEntityId);
            contextInfoDetails.Add("PrimaryEntityName: " + context.PrimaryEntityName);
            contextInfoDetails.Add("InitiatingUserId: " + context.InitiatingUserId);
            contextInfoDetails.Add("Depth: " + context.Depth);
            contextInfoDetails.Add("Stage: " + context.Stage);
            contextInfoDetails.Add("CorrelationId: " + context.CorrelationId);
            contextInfoDetails.Add("IsInTransaction: " + context.IsInTransaction);
            contextInfoDetails.Add("Mode: " + context.Mode);
            contextInfoDetails.Add("OperationCreatedOn: " + context.OperationCreatedOn);
            contextInfoDetails.Add("OperationId: " + context.OperationId);
            contextInfoDetails.Add("Organization: " + context.OrganizationName + " : " + context.OrganizationId);

            if (target == null || target.Attributes.Count == 0)
            {
                contextInfoDetails.Add("Target: N/A");
            }
            else
            {
                contextInfoDetails.Add("Target: " + target.LogicalName + "(" + target.Id + ")");
                contextInfoDetails.AddRange(target.Attributes.Select(att => "    Entity[" + att.Key + "]: " + att.Value));
            }

            var contextInfo = String.Join(Environment.NewLine, contextInfoDetails);
            return contextInfo;
        }

        /// <summary>
        /// Gets the plugin "Target" Entity Object.
        /// </summary>
        /// <param name="context">The context.</param>
        /// <returns></returns>
        private static Entity GetTarget(IPluginExecutionContext context)
        {
            Entity entity = null;
            // The InputParameters collection contains all the data passed in the message request.
            if (context.InputParameters.Contains("Target") && context.InputParameters["Target"] is Entity)
            {
                // Obtain the target entity from the input parameters.
                entity = (Entity)context.InputParameters["Target"];
            }
            else if (context.InputParameters.Contains("Target") && context.InputParameters["Target"] is EntityReference)
            {
                // Obtain the target entity from the input parameters.
                var entityRef = (EntityReference)context.InputParameters["Target"];

                // Create basic Entity from EntityReference.
                entity = new Entity(entityRef.LogicalName) { Id = entityRef.Id };
            }

            return entity;
        }
    }
}
